/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: SyntaxTreeNode.java,v 1.6 2006/06/06 22:34:33 spericas Exp $
 */

package com.sun.org.apache.xalan.internal.xsltc.compiler;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
import com.sun.org.apache.bcel.internal.generic.BasicType;
import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
import com.sun.org.apache.bcel.internal.generic.DUP_X1;
import com.sun.org.apache.bcel.internal.generic.GETFIELD;
import com.sun.org.apache.bcel.internal.generic.ICONST;
import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
import com.sun.org.apache.bcel.internal.generic.InstructionList;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.bcel.internal.generic.NEWARRAY;
import com.sun.org.apache.bcel.internal.generic.PUSH;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;


/**
 * @author Jacek Ambroziak
 * @author Santiago Pericas-Geertsen
 * @author G. Todd Miller
 * @author Morten Jorensen
 * @author Erwin Bolwidt <ejb@klomp.org>
 * @author John Howard <JohnH@schemasoft.com>
 */
public abstract class SyntaxTreeNode implements Constants {

  // Reference to the AST parser
  private Parser _parser;

  // AST navigation pointers
  protected SyntaxTreeNode _parent;          // Parent node
  private Stylesheet _stylesheet;      // Stylesheet ancestor node
  private Template _template;        // Template ancestor node
  private final Vector _contents = new Vector(2); // Child nodes

  // Element description data
  protected QName _qname;                    // The element QName
  private int _line;                         // Source file line number
  protected AttributesImpl _attributes = null;   // Attributes of this element
  private Hashtable _prefixMapping = null; // Namespace declarations

  // Sentinel - used to denote unrecognised syntaxt tree nodes.
  protected static final SyntaxTreeNode Dummy = new AbsolutePathPattern(null);

  // These two are used for indenting nodes in the AST (debug output)
  protected static final int IndentIncrement = 4;
  private static final char[] _spaces =
      "                                                       ".toCharArray();

  /**
   * Creates a new SyntaxTreeNode with a 'null' QName and no source file
   * line number reference.
   */
  public SyntaxTreeNode() {
    _line = 0;
    _qname = null;
  }

  /**
   * Creates a new SyntaxTreeNode with a 'null' QName.
   *
   * @param line Source file line number reference
   */
  public SyntaxTreeNode(int line) {
    _line = line;
    _qname = null;
  }

  /**
   * Creates a new SyntaxTreeNode with no source file line number reference.
   *
   * @param uri The element's namespace URI
   * @param prefix The element's namespace prefix
   * @param local The element's local name
   */
  public SyntaxTreeNode(String uri, String prefix, String local) {
    _line = 0;
    setQName(uri, prefix, local);
  }

  /**
   * Set the source file line number for this element
   *
   * @param line The source file line number.
   */
  protected final void setLineNumber(int line) {
    _line = line;
  }

  /**
   * Get the source file line number for this element. If unavailable, lookup
   * in ancestors.
   *
   * @return The source file line number.
   */
  public final int getLineNumber() {
    if (_line > 0) {
      return _line;
    }
    SyntaxTreeNode parent = getParent();
    return (parent != null) ? parent.getLineNumber() : 0;
  }

  /**
   * Set the QName for the syntax tree node.
   *
   * @param qname The QName for the syntax tree node
   */
  protected void setQName(QName qname) {
    _qname = qname;
  }

  /**
   * Set the QName for the SyntaxTreeNode
   *
   * @param uri The element's namespace URI
   * @param prefix The element's namespace prefix
   * @param local The element's local name
   */
  protected void setQName(String uri, String prefix, String localname) {
    _qname = new QName(uri, prefix, localname);
  }

  /**
   * Set the QName for the SyntaxTreeNode
   *
   * @param qname The QName for the syntax tree node
   */
  protected QName getQName() {
    return (_qname);
  }

  /**
   * Set the attributes for this SyntaxTreeNode.
   *
   * @param attributes Attributes for the element. Must be passed in as an implementation of
   * org.xml.sax.Attributes.
   */
  protected void setAttributes(AttributesImpl attributes) {
    _attributes = attributes;
  }

  /**
   * Returns a value for an attribute from the source element.
   *
   * @param qname The QName of the attribute to return.
   * @return The value of the attribute of name 'qname'.
   */
  protected String getAttribute(String qname) {
    if (_attributes == null) {
      return EMPTYSTRING;
    }
    final String value = _attributes.getValue(qname);
    return (value == null || value.equals(EMPTYSTRING)) ?
        EMPTYSTRING : value;
  }

  protected String getAttribute(String prefix, String localName) {
    return getAttribute(prefix + ':' + localName);
  }

  protected boolean hasAttribute(String qname) {
    return (_attributes != null && _attributes.getValue(qname) != null);
  }

  protected void addAttribute(String qname, String value) {
    int index = _attributes.getIndex(qname);
    if (index != -1) {
      _attributes.setAttribute(index, "", Util.getLocalName(qname),
          qname, "CDATA", value);
    } else {
      _attributes.addAttribute("", Util.getLocalName(qname), qname,
          "CDATA", value);
    }
  }

  /**
   * Returns a list of all attributes declared for the element represented by
   * this syntax tree node.
   *
   * @return Attributes for this syntax tree node
   */
  protected Attributes getAttributes() {
    return (_attributes);
  }

  /**
   * Sets the prefix mapping for the namespaces that were declared in this
   * element. This does not include all prefix mappings in scope, so one
   * may have to check ancestor elements to get all mappings that are in
   * in scope. The prefixes must be passed in as a Hashtable that maps
   * namespace prefixes (String objects) to namespace URIs (also String).
   *
   * @param mapping The Hashtable containing the mappings.
   */
  protected void setPrefixMapping(Hashtable mapping) {
    _prefixMapping = mapping;
  }

  /**
   * Returns a Hashtable containing the prefix mappings that were declared
   * for this element. This does not include all prefix mappings in scope,
   * so one may have to check ancestor elements to get all mappings that are
   * in in scope.
   *
   * @return Prefix mappings (for this element only).
   */
  protected Hashtable getPrefixMapping() {
    return _prefixMapping;
  }

  /**
   * Adds a single prefix mapping to this syntax tree node.
   *
   * @param prefix Namespace prefix.
   * @param uri Namespace URI.
   */
  protected void addPrefixMapping(String prefix, String uri) {
    if (_prefixMapping == null) {
      _prefixMapping = new Hashtable();
    }
    _prefixMapping.put(prefix, uri);
  }

  /**
   * Returns any namespace URI that is in scope for a given prefix. This
   * method checks namespace mappings for this element, and if necessary
   * for ancestor elements as well (ie. if the prefix maps to an URI in this
   * scope then you'll definately get the URI from this method).
   *
   * @param prefix Namespace prefix.
   * @return Namespace URI.
   */
  protected String lookupNamespace(String prefix) {
    // Initialise the output (default is 'null' for undefined)
    String uri = null;

    // First look up the prefix/uri mapping in our own hashtable...
    if (_prefixMapping != null) {
      uri = (String) _prefixMapping.get(prefix);
    }
    // ... but if we can't find it there we ask our parent for the mapping
    if ((uri == null) && (_parent != null)) {
      uri = _parent.lookupNamespace(prefix);
      if ((prefix == Constants.EMPTYSTRING) && (uri == null)) {
        uri = Constants.EMPTYSTRING;
      }
    }
    // ... and then we return whatever URI we've got.
    return (uri);
  }

  /**
   * Returns any namespace prefix that is mapped to a prefix in the current
   * scope. This method checks namespace mappings for this element, and if
   * necessary for ancestor elements as well (ie. if the URI is declared
   * within the current scope then you'll definately get the prefix from
   * this method). Note that this is a very slow method and consequentially
   * it should only be used strictly when needed.
   *
   * @param uri Namespace URI.
   * @return Namespace prefix.
   */
  protected String lookupPrefix(String uri) {
    // Initialise the output (default is 'null' for undefined)
    String prefix = null;

    // First look up the prefix/uri mapping in our own hashtable...
    if ((_prefixMapping != null) &&
        (_prefixMapping.contains(uri))) {
      Enumeration prefixes = _prefixMapping.keys();
      while (prefixes.hasMoreElements()) {
        prefix = (String) prefixes.nextElement();
        String mapsTo = (String) _prefixMapping.get(prefix);
        if (mapsTo.equals(uri)) {
          return (prefix);
        }
      }
    }
    // ... but if we can't find it there we ask our parent for the mapping
    else if (_parent != null) {
      prefix = _parent.lookupPrefix(uri);
      if ((uri == Constants.EMPTYSTRING) && (prefix == null)) {
        prefix = Constants.EMPTYSTRING;
      }
    }
    return (prefix);
  }

  /**
   * Set this node's parser. The parser (the XSLT parser) gives this
   * syntax tree node access to the symbol table and XPath parser.
   *
   * @param parser The XSLT parser.
   */
  protected void setParser(Parser parser) {
    _parser = parser;
  }

  /**
   * Returns this node's XSLT parser.
   *
   * @return The XSLT parser.
   */
  public final Parser getParser() {
    return _parser;
  }

  /**
   * Set this syntax tree node's parent node, if unset. For
   * re-parenting just use <code>node._parent = newparent</code>.
   *
   * @param parent The parent node.
   */
  protected void setParent(SyntaxTreeNode parent) {
    if (_parent == null) {
      _parent = parent;
    }
  }

  /**
   * Returns this syntax tree node's parent node.
   *
   * @return The parent syntax tree node.
   */
  protected final SyntaxTreeNode getParent() {
    return _parent;
  }

  /**
   * Returns 'true' if this syntax tree node is the Sentinal node.
   *
   * @return 'true' if this syntax tree node is the Sentinal node.
   */
  protected final boolean isDummy() {
    return this == Dummy;
  }

  /**
   * Get the import precedence of this element. The import precedence equals
   * the import precedence of the stylesheet in which this element occured.
   *
   * @return The import precedence of this syntax tree node.
   */
  protected int getImportPrecedence() {
    Stylesheet stylesheet = getStylesheet();
    if (stylesheet == null) {
      return Integer.MIN_VALUE;
    }
    return stylesheet.getImportPrecedence();
  }

  /**
   * Get the Stylesheet node that represents the <xsl:stylesheet/> element
   * that this node occured under.
   *
   * @return The Stylesheet ancestor node of this node.
   */
  public Stylesheet getStylesheet() {
    if (_stylesheet == null) {
      SyntaxTreeNode parent = this;
      while (parent != null) {
        if (parent instanceof Stylesheet) {
          return ((Stylesheet) parent);
        }
        parent = parent.getParent();
      }
      _stylesheet = (Stylesheet) parent;
    }
    return (_stylesheet);
  }

  /**
   * Get the Template node that represents the <xsl:template/> element
   * that this node occured under. Note that this method will return 'null'
   * for nodes that represent top-level elements.
   *
   * @return The Template ancestor node of this node or 'null'.
   */
  protected Template getTemplate() {
    if (_template == null) {
      SyntaxTreeNode parent = this;
      while ((parent != null) && (!(parent instanceof Template))) {
        parent = parent.getParent();
      }
      _template = (Template) parent;
    }
    return (_template);
  }

  /**
   * Returns a reference to the XSLTC (XSLT compiler) in use.
   *
   * @return XSLTC - XSLT compiler.
   */
  protected final XSLTC getXSLTC() {
    return _parser.getXSLTC();
  }

  /**
   * Returns the XSLT parser's symbol table.
   *
   * @return Symbol table.
   */
  protected final SymbolTable getSymbolTable() {
    return (_parser == null) ? null : _parser.getSymbolTable();
  }

  /**
   * Parse the contents of this syntax tree nodes (child nodes, XPath
   * expressions, patterns and functions). The default behaviour is to parser
   * the syntax tree node's children (since there are no common expressions,
   * patterns, etc. that can be handled in this base class.
   *
   * @param parser reference to the XSLT parser
   */
  public void parseContents(Parser parser) {
    parseChildren(parser);
  }

  /**
   * Parse all children of this syntax tree node. This method is normally
   * called by the parseContents() method.
   *
   * @param parser reference to the XSLT parser
   */
  protected final void parseChildren(Parser parser) {

    Vector locals = null;   // only create when needed

    final int count = _contents.size();
    for (int i = 0; i < count; i++) {
      SyntaxTreeNode child = (SyntaxTreeNode) _contents.elementAt(i);
      parser.getSymbolTable().setCurrentNode(child);
      child.parseContents(parser);
      // if variable or parameter, add it to scope
      final QName varOrParamName = updateScope(parser, child);
      if (varOrParamName != null) {
        if (locals == null) {
          locals = new Vector(2);
        }
        locals.addElement(varOrParamName);
      }
    }

    parser.getSymbolTable().setCurrentNode(this);

    // after the last element, remove any locals from scope
    if (locals != null) {
      final int nLocals = locals.size();
      for (int i = 0; i < nLocals; i++) {
        parser.removeVariable((QName) locals.elementAt(i));
      }
    }
  }

  /**
   * Add a node to the current scope and return name of a variable or
   * parameter if the node represents a variable or a parameter.
   */
  protected QName updateScope(Parser parser, SyntaxTreeNode node) {
    if (node instanceof Variable) {
      final Variable var = (Variable) node;
      parser.addVariable(var);
      return var.getName();
    } else if (node instanceof Param) {
      final Param param = (Param) node;
      parser.addParameter(param);
      return param.getName();
    } else {
      return null;
    }
  }

  /**
   * Type check the children of this node. The type check phase may add
   * coercions (CastExpr) to the AST.
   *
   * @param stable The compiler/parser's symbol table
   */
  public abstract Type typeCheck(SymbolTable stable) throws TypeCheckError;

  /**
   * Call typeCheck() on all child syntax tree nodes.
   *
   * @param stable The compiler/parser's symbol table
   */
  protected Type typeCheckContents(SymbolTable stable) throws TypeCheckError {
    final int n = elementCount();
    for (int i = 0; i < n; i++) {
      SyntaxTreeNode item = (SyntaxTreeNode) _contents.elementAt(i);
      item.typeCheck(stable);
    }
    return Type.Void;
  }

  /**
   * Translate this abstract syntax tree node into JVM bytecodes.
   *
   * @param classGen BCEL Java class generator
   * @param methodGen BCEL Java method generator
   */
  public abstract void translate(ClassGenerator classGen,
      MethodGenerator methodGen);

  /**
   * Call translate() on all child syntax tree nodes.
   *
   * @param classGen BCEL Java class generator
   * @param methodGen BCEL Java method generator
   */
  protected void translateContents(ClassGenerator classGen,
      MethodGenerator methodGen) {
    // Call translate() on all child nodes
    final int n = elementCount();

    for (int i = 0; i < n; i++) {
      methodGen.markChunkStart();
      final SyntaxTreeNode item = (SyntaxTreeNode) _contents.elementAt(i);
      item.translate(classGen, methodGen);
      methodGen.markChunkEnd();
    }

    // After translation, unmap any registers for any variables/parameters
    // that were declared in this scope. Performing this unmapping in the
    // same AST scope as the declaration deals with the problems of
    // references falling out-of-scope inside the for-each element.
    // (the cause of which being 'lazy' register allocation for references)
    for (int i = 0; i < n; i++) {
      if (_contents.elementAt(i) instanceof VariableBase) {
        final VariableBase var = (VariableBase) _contents.elementAt(i);
        var.unmapRegister(methodGen);
      }
    }
  }

  /**
   * Return true if the node represents a simple RTF.
   *
   * A node is a simple RTF if all children only produce Text value.
   *
   * @param node A node
   * @return true if the node content can be considered as a simple RTF.
   */
  private boolean isSimpleRTF(SyntaxTreeNode node) {

    Vector contents = node.getContents();
    for (int i = 0; i < contents.size(); i++) {
      SyntaxTreeNode item = (SyntaxTreeNode) contents.elementAt(i);
      if (!isTextElement(item, false)) {
        return false;
      }
    }

    return true;
  }

  /**
   * Return true if the node represents an adaptive RTF.
   *
   * A node is an adaptive RTF if each children is a Text element
   * or it is <xsl:call-template> or <xsl:apply-templates>.
   *
   * @param node A node
   * @return true if the node content can be considered as an adaptive RTF.
   */
  private boolean isAdaptiveRTF(SyntaxTreeNode node) {

    Vector contents = node.getContents();
    for (int i = 0; i < contents.size(); i++) {
      SyntaxTreeNode item = (SyntaxTreeNode) contents.elementAt(i);
      if (!isTextElement(item, true)) {
        return false;
      }
    }

    return true;
  }

  /**
   * Return true if the node only produces Text content.
   *
   * A node is a Text element if it is Text, xsl:value-of, xsl:number,
   * or a combination of these nested in a control instruction (xsl:if or
   * xsl:choose).
   *
   * If the doExtendedCheck flag is true, xsl:call-template and xsl:apply-templates
   * are also considered as Text elements.
   *
   * @param node A node
   * @param doExtendedCheck If this flag is true, <xsl:call-template> and <xsl:apply-templates> are
   * also considered as Text elements.
   * @return true if the node of Text type
   */
  private boolean isTextElement(SyntaxTreeNode node, boolean doExtendedCheck) {
    if (node instanceof ValueOf || node instanceof Number
        || node instanceof Text) {
      return true;
    } else if (node instanceof If) {
      return doExtendedCheck ? isAdaptiveRTF(node) : isSimpleRTF(node);
    } else if (node instanceof Choose) {
      Vector contents = node.getContents();
      for (int i = 0; i < contents.size(); i++) {
        SyntaxTreeNode item = (SyntaxTreeNode) contents.elementAt(i);
        if (item instanceof Text ||
            ((item instanceof When || item instanceof Otherwise)
                && ((doExtendedCheck && isAdaptiveRTF(item))
                || (!doExtendedCheck && isSimpleRTF(item))))) {
          continue;
        } else {
          return false;
        }
      }
      return true;
    } else if (doExtendedCheck &&
        (node instanceof CallTemplate
            || node instanceof ApplyTemplates)) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * Utility method used by parameters and variables to store result trees
   *
   * @param classGen BCEL Java class generator
   * @param methodGen BCEL Java method generator
   */
  protected void compileResultTree(ClassGenerator classGen,
      MethodGenerator methodGen) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = methodGen.getInstructionList();
    final Stylesheet stylesheet = classGen.getStylesheet();

    boolean isSimple = isSimpleRTF(this);
    boolean isAdaptive = false;
    if (!isSimple) {
      isAdaptive = isAdaptiveRTF(this);
    }

    int rtfType = isSimple ? DOM.SIMPLE_RTF
        : (isAdaptive ? DOM.ADAPTIVE_RTF : DOM.TREE_RTF);

    // Save the current handler base on the stack
    il.append(methodGen.loadHandler());

    final String DOM_CLASS = classGen.getDOMClass();

    // Create new instance of DOM class (with RTF_INITIAL_SIZE nodes)
    //int index = cpg.addMethodref(DOM_IMPL, "<init>", "(I)V");
    //il.append(new NEW(cpg.addClass(DOM_IMPL)));

    il.append(methodGen.loadDOM());
    int index = cpg.addInterfaceMethodref(DOM_INTF,
        "getResultTreeFrag",
        "(IIZ)" + DOM_INTF_SIG);
    il.append(new PUSH(cpg, RTF_INITIAL_SIZE));
    il.append(new PUSH(cpg, rtfType));
    il.append(new PUSH(cpg, stylesheet.callsNodeset()));
    il.append(new INVOKEINTERFACE(index, 4));

    il.append(DUP);

    // Overwrite old handler with DOM handler
    index = cpg.addInterfaceMethodref(DOM_INTF,
        "getOutputDomBuilder",
        "()" + TRANSLET_OUTPUT_SIG);

    il.append(new INVOKEINTERFACE(index, 1));
    il.append(DUP);
    il.append(methodGen.storeHandler());

    // Call startDocument on the new handler
    il.append(methodGen.startDocument());

    // Instantiate result tree fragment
    translateContents(classGen, methodGen);

    // Call endDocument on the new handler
    il.append(methodGen.loadHandler());
    il.append(methodGen.endDocument());

    // Check if we need to wrap the DOMImpl object in a DOMAdapter object.
    // DOMAdapter is not needed if the RTF is a simple RTF and the nodeset()
    // function is not used.
    if (stylesheet.callsNodeset()
        && !DOM_CLASS.equals(DOM_IMPL_CLASS)) {
      // new com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter(DOMImpl,String[]);
      index = cpg.addMethodref(DOM_ADAPTER_CLASS,
          "<init>",
          "(" + DOM_INTF_SIG +
              "[" + STRING_SIG +
              "[" + STRING_SIG +
              "[I" +
              "[" + STRING_SIG + ")V");
      il.append(new NEW(cpg.addClass(DOM_ADAPTER_CLASS)));
      il.append(new DUP_X1());
      il.append(SWAP);

            /*
             * Give the DOM adapter an empty type mapping if the nodeset
             * extension function is never called.
             */
      if (!stylesheet.callsNodeset()) {
        il.append(new ICONST(0));
        il.append(new ANEWARRAY(cpg.addClass(STRING)));
        il.append(DUP);
        il.append(DUP);
        il.append(new ICONST(0));
        il.append(new NEWARRAY(BasicType.INT));
        il.append(SWAP);
        il.append(new INVOKESPECIAL(index));
      } else {
        // Push name arrays on the stack
        il.append(ALOAD_0);
        il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
            NAMES_INDEX,
            NAMES_INDEX_SIG)));
        il.append(ALOAD_0);
        il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
            URIS_INDEX,
            URIS_INDEX_SIG)));
        il.append(ALOAD_0);
        il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
            TYPES_INDEX,
            TYPES_INDEX_SIG)));
        il.append(ALOAD_0);
        il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
            NAMESPACE_INDEX,
            NAMESPACE_INDEX_SIG)));

        // Initialized DOM adapter
        il.append(new INVOKESPECIAL(index));

        // Add DOM adapter to MultiDOM class by calling addDOMAdapter()
        il.append(DUP);
        il.append(methodGen.loadDOM());
        il.append(new CHECKCAST(cpg.addClass(classGen.getDOMClass())));
        il.append(SWAP);
        index = cpg.addMethodref(MULTI_DOM_CLASS,
            "addDOMAdapter",
            "(" + DOM_ADAPTER_SIG + ")I");
        il.append(new INVOKEVIRTUAL(index));
        il.append(POP);         // ignore mask returned by addDOMAdapter
      }
    }

    // Restore old handler base from stack
    il.append(SWAP);
    il.append(methodGen.storeHandler());
  }

  /**
   * Returns true if this expression/instruction depends on the context. By
   * default, every expression/instruction depends on the context unless it
   * overrides this method. Currently used to determine if result trees are
   * compiled using procedures or little DOMs (result tree fragments).
   *
   * @return 'true' if this node depends on the context.
   */
  protected boolean contextDependent() {
    return true;
  }

  /**
   * Return true if any of the expressions/instructions in the contents of
   * this node is context dependent.
   *
   * @return 'true' if the contents of this node is context dependent.
   */
  protected boolean dependentContents() {
    final int n = elementCount();
    for (int i = 0; i < n; i++) {
      final SyntaxTreeNode item = (SyntaxTreeNode) _contents.elementAt(i);
      if (item.contextDependent()) {
        return true;
      }
    }
    return false;
  }

  /**
   * Adds a child node to this syntax tree node.
   *
   * @param element is the new child node.
   */
  protected final void addElement(SyntaxTreeNode element) {
    _contents.addElement(element);
    element.setParent(this);
  }

  /**
   * Inserts the first child node of this syntax tree node. The existing
   * children are shifted back one position.
   *
   * @param element is the new child node.
   */
  protected final void setFirstElement(SyntaxTreeNode element) {
    _contents.insertElementAt(element, 0);
    element.setParent(this);
  }

  /**
   * Removed a child node of this syntax tree node.
   *
   * @param element is the child node to remove.
   */
  protected final void removeElement(SyntaxTreeNode element) {
    _contents.remove(element);
    element.setParent(null);
  }

  /**
   * Returns a Vector containing all the child nodes of this node.
   *
   * @return A Vector containing all the child nodes of this node.
   */
  protected final Vector getContents() {
    return _contents;
  }

  /**
   * Tells you if this node has any child nodes.
   *
   * @return 'true' if this node has any children.
   */
  protected final boolean hasContents() {
    return elementCount() > 0;
  }

  /**
   * Returns the number of children this node has.
   *
   * @return Number of child nodes.
   */
  protected final int elementCount() {
    return _contents.size();
  }

  /**
   * Returns an Enumeration of all child nodes of this node.
   *
   * @return An Enumeration of all child nodes of this node.
   */
  protected final Enumeration elements() {
    return _contents.elements();
  }

  /**
   * Returns a child node at a given position.
   *
   * @param pos The child node's position.
   * @return The child node.
   */
  protected final Object elementAt(int pos) {
    return _contents.elementAt(pos);
  }

  /**
   * Returns this element's last child
   *
   * @return The child node.
   */
  protected final SyntaxTreeNode lastChild() {
    if (_contents.size() == 0) {
      return null;
    }
    return (SyntaxTreeNode) _contents.lastElement();
  }

  /**
   * Displays the contents of this syntax tree node (to stdout).
   * This method is intended for debugging _only_, and should be overridden
   * by all syntax tree node implementations.
   *
   * @param indent Indentation level for syntax tree levels.
   */
  public void display(int indent) {
    displayContents(indent);
  }

  /**
   * Displays the contents of this syntax tree node (to stdout).
   * This method is intended for debugging _only_ !!!
   *
   * @param indent Indentation level for syntax tree levels.
   */
  protected void displayContents(int indent) {
    final int n = elementCount();
    for (int i = 0; i < n; i++) {
      SyntaxTreeNode item = (SyntaxTreeNode) _contents.elementAt(i);
      item.display(indent);
    }
  }

  /**
   * Set the indentation level for debug output.
   *
   * @param indent Indentation level for syntax tree levels.
   */
  protected final void indent(int indent) {
    System.out.print(new String(_spaces, 0, indent));
  }

  /**
   * Report an error to the parser.
   *
   * @param element The element in which the error occured (normally 'this' but it could also be an
   * expression/pattern/etc.)
   * @param parser The XSLT parser to report the error to.
   * @param error The error code (from util/ErrorMsg).
   * @param message Any additional error message.
   */
  protected void reportError(SyntaxTreeNode element, Parser parser,
      String errorCode, String message) {
    final ErrorMsg error = new ErrorMsg(errorCode, message, element);
    parser.reportError(Constants.ERROR, error);
  }

  /**
   * Report a recoverable error to the parser.
   *
   * @param element The element in which the error occured (normally 'this' but it could also be an
   * expression/pattern/etc.)
   * @param parser The XSLT parser to report the error to.
   * @param error The error code (from util/ErrorMsg).
   * @param message Any additional error message.
   */
  protected void reportWarning(SyntaxTreeNode element, Parser parser,
      String errorCode, String message) {
    final ErrorMsg error = new ErrorMsg(errorCode, message, element);
    parser.reportError(Constants.WARNING, error);
  }

}
